﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Zo.Tasks;

namespace Zo.Events.Core
{
    /// <summary>
    /// 事件执行者。
    /// </summary>
    internal interface IEventExecutor
    {
        /// <summary>
        /// 事件名。
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="envelope">事件。</param>
        void Handle(object envelope);

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="envelope">事件。</param>
        /// <returns>处理结果。</returns>
        Task<IEventResult> HandleAsync(object envelope);
    }

    /// <summary>
    /// 事件执行者。
    /// </summary>
    internal class DefaultEventExecutor : IEventExecutor
    {
        #region 字段

        /// <summary>
        /// 状态。
        /// </summary>
        private int _status = EventStatus.Idle;

        /// <summary>
        /// 事件队列。
        /// </summary>
        private readonly EventQueue _queue = new ConcurrentEventQueue();

        /// <summary>
        /// 结果集。
        /// </summary>
        private readonly IDictionary<Guid, TaskCompletionSource<IEventResult>> _eventResults = new Dictionary<Guid, TaskCompletionSource<IEventResult>>();

        /// <summary>
        /// 事件处理器。
        /// </summary>
        private readonly IEventHandler _eventHandler;

        #endregion 字段

        #region 初始化

        /// <summary>
        /// 初始化事件执行者。
        /// </summary>
        /// <param name="name">事件名。</param>
        /// <param name="eventHandler">事件处理器。</param>
        public DefaultEventExecutor(string name, IEventHandler eventHandler)
        {
            Name = name;
            _eventHandler = eventHandler;
        }

        #endregion 初始化

        #region 属性

        /// <summary>
        /// 事件名。
        /// </summary>
        public string Name { get; }

        #endregion 属性

        #region 事件处理

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="message">事件。</param>
        public void Handle(object message)
        {
            OnHandle(new EventEnvelope { Message = message, Type = EnvelopeType.None });
        }

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="message">事件。</param>
        /// <returns>处理结果。</returns>
        public Task<IEventResult> HandleAsync(object message)
        {
            var envelope = new EventEnvelope { Message = message, Type = EnvelopeType.Return };

            var taskCompletionSource = new TaskCompletionSource<IEventResult>();

            _eventResults[envelope.Id] = taskCompletionSource;

            OnHandle(envelope);

            return taskCompletionSource.Task;
        }

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="envelope">事件消息。</param>
        private void OnHandle(EventEnvelope envelope)
        {
            _queue.Enqueue(envelope);

            Schedule();
        }

        /// <summary>
        /// 事件处理。
        /// </summary>
        /// <param name="envelope">事件信息。</param>
        /// <remarks>处理结果。</remarks>
        public IEventResult OnExeucte(object envelope)
        {
            return _eventHandler.Handle(envelope);
        }

        #endregion 事件处理

        #region 任务调度

        /// <summary>
        /// 任务调度。
        /// </summary>
        private void Schedule()
        {
            if (_status == EventStatus.Exit) return;

            var status = Interlocked.CompareExchange(ref _status, EventStatus.Busy, EventStatus.Idle);

            if (status == EventStatus.Idle)
            {
                //将消息放入微软提供的线程池。
                ThreadTask.Current.AddTask(Execute, null);
            }
        }

        /// <summary>
        /// 线程池处理。
        /// </summary>
        private void Execute(object state)
        {
            if (_queue.Count <= 0)
                return;

            //取出消息。
            var envelope = _queue.Dequeue();

            IEventResult eventResult;

            try
            {
                eventResult = OnExeucte(envelope.Message);
            }
            catch (Exception exception)
            {
                eventResult = new EventResult { Exception = exception };
            }

            if (envelope.Type == EnvelopeType.Return)
            {
                //返回结果。
                if (_eventResults.ContainsKey(envelope.Id))
                    _eventResults[envelope.Id].SetResult(eventResult);
            }

            if (_status == EventStatus.Exit)
            {
                //如果用户调用了Actor.Exit方法，那么它的Exited属性则会返回true，我们可以将_status设为 Exit，这样Actor再也不会回到 Idle 状态，也就避免了无谓的资源分配。
                Thread.VolatileWrite(ref _status, EventStatus.Exit);
            }
            else
            {
                /*
                如果Actor没有退出，那么它会被短暂地切换为 Idle 状态。
                此后如果Actor的邮箱中存在剩余的消息，那么我们会再次调用 Schedule 方法“尝试”再次为 Actor 分配运算资源。
                */
                Thread.VolatileWrite(ref _status, EventStatus.Idle);

                if (_queue.Count > 0)
                    Schedule();
            }
        }

        /// <summary>
        /// 参与者状态。
        /// </summary>
        private struct EventStatus
        {
            /// <summary>
            /// 空闲。
            /// </summary>
            public const int Idle = 0;

            /// <summary>
            /// 忙碌。
            /// </summary>
            public const int Busy = 1;

            /// <summary>
            /// 退出。
            /// </summary>
            public const int Exit = 2;
        }

        #endregion 任务调度
    }
}